package main

import (
	"context"
	"errors"
	"github.com/gin-gonic/gin"
	"go.uber.org/zap"
	"golang.org/x/sync/errgroup"
	"net"
	"net/http"
	"os"
	"path/filepath"
	"sync"
)

// Config defines configuration for debug server.
type Config struct {
	BaseUrl          string `help:"admin peer http listening address" devDefault:"http://localhost:8889" default:"http://123.207.34.45:10008"`
	Address          string `help:"admin peer http listening address" devDefault:"0.0.0.0:8889" default:"0.0.0.0:10008"`
	PkgList          string `help:"包列表记录文件" devDefault:"$HOME/pkg_list.json" default:"$ROOT/pkg_release.json"`
	PkgReleaseLog    string `help:"包发布日志" devDefault:"$HOME/pkg_release.log" default:"$ROOT/pkg_list.log"`
	UpgradeReportLog string `help:"升级日志" devDefault:"$HOME/upgrade_report.log" default:"$ROOT/upgrade_report.log"`
	PkgRoot          string `help:"node包存放地址" devDefault:"$HOME" default:"$ROOT/node"`
	MaxUploadSize    int64  `help:"最大上传限制,默认100m" default:"100000000"`
}

// Server provides endpoints for administrative tasks.
type Server struct {
	mux    sync.Mutex
	log    *zap.Logger
	server http.Server
	config Config

	pkgList          *PkgList
	pkgReleaseLog    *PkgReleaseLog
	upgradeReportLog *UpgradeReportLog
}

// NewServer returns a new administration Server.
func NewServer(log *zap.Logger, config Config) (*Server, error) {
	prl, err := NewPkgReleaseLog(config.PkgReleaseLog)
	if err != nil {
		return nil, err
	}
	pl, err := NewPkgList(config.PkgList)
	if err != nil {
		return nil, err
	}
	url, err := NewUpgradeReportLog(config.UpgradeReportLog)
	if err != nil {
		return nil, err
	}
	s := &Server{
		log:    log,
		config: config,

		pkgList:          pl,
		pkgReleaseLog:    prl,
		upgradeReportLog: url,
	}
	engine := gin.Default()
	engine.MaxMultipartMemory = config.MaxUploadSize
	engine.POST("/pkg/release", s.release)               //推送发布包
	engine.GET("/pkg/:file", s.download)                 //现在程序包
	engine.POST("/node/check_upgrade", s.checkUpgrade)   //校验是否需要升级
	engine.POST("/node/upgrade_report", s.upgradeReport) //自动升级结果报告
	engine.POST("/node/latest", s.latest)                //获取最新节点包
	s.server.Handler = engine
	return s, nil
}

// Run starts the admin endpoint.
func (s *Server) Run(ctx context.Context) error {
	listener, err := net.Listen("tcp", s.config.Address)
	if err != nil {
		return err
	}
	ctx, cancel := context.WithCancel(ctx)
	var group errgroup.Group
	group.Go(func() error {
		<-ctx.Done()
		return s.server.Shutdown(context.Background())
	})
	group.Go(func() error {
		defer cancel()
		_err := s.server.Serve(listener)
		if errors.Is(_err, http.ErrServerClosed) {
			_err = nil
		}
		return _err
	})
	s.log.Info("node_upgrade start: " + s.config.Address)
	return group.Wait()
}

// Close closes server and underlying listener.
func (s *Server) Close() error {
	_ = s.pkgReleaseLog.Close()
	_ = s.upgradeReportLog.Close()
	return s.server.Close()
}

func openFile(path string) (*os.File, error) {
	fi, err := os.Stat(path)
	if err == nil {
		if fi.IsDir() {
			return nil, errors.New(path + " is dir ")
		}
		return os.OpenFile(path, os.O_RDWR|os.O_APPEND, 0666)
	} else {
		dir := filepath.Dir(path)
		fi, err := os.Stat(dir)
		if err != nil {
			err = os.MkdirAll(dir, 0666)
			if err != nil {
				return nil, err
			}
		} else if !fi.IsDir() {
			return nil, errors.New(dir + " is not dir ")
		}
		return os.Create(path)
	}
}

func (s *Server) successJson(ctx *gin.Context, data any) {
	ctx.AbortWithStatusJSON(200, gin.H{
		"code":    0,
		"message": "success",
		"data":    data,
	})
}

func (s *Server) errorJson(ctx *gin.Context, err error) {
	s.log.Error("request error", zap.Error(err))
	ctx.AbortWithStatusJSON(200, gin.H{
		"code":    1,
		"message": err.Error(),
	})
}
